package com.timber.util;


import java.io.*;
import java.math.BigDecimal;
import java.util.*;
import javax.swing.table.*;
import javax.swing.JLabel;
import java.awt.datatransfer.*;
import java.awt.Toolkit;
import java.text.*;
import java.awt.Color;
import javax.swing.event.*;

import pv.jfcx.*;
import pv.util.*;

import com.f1j.ss.*;
import com.f1j.util.*;

/**
 *
 *	Abstract Custom table model class
 *
 *	Must be extended to provide list of columns
 *	getFieldAt( Object obj, int c ) to display data
 *	setFieldAt( Object o, Object item, int c ) to edit data
 *
 *	row vectors are created by genTableRowVector()
 */
public abstract class SLTableModel extends PVTableModelNew implements SLDataChangeListener
{
  /**
   * value representing empty cell when string is required
   */
  public final static String EMPTY_CELL = "~";

  //  Concrete sub classes need to initialize this with their own
  //  flavor of column manager
  protected static SLColumnManager		cm;

  /**	actual Column Manager for the table	*/
  protected SLColumnManager	colMgr = null;

  //	default sort columns - NOTE: Only up to the first 3 descriptors will be
  //	considered since JPVTable's can only sort a max of 3 columns
  protected ColumnSortDescriptor[]	defaultSortColumns = null;

  protected int				minRows;
  protected int				numCols;
  protected Vector			rowData = new Vector();

  //	complete list of possible table columns
  //	list of CLONED SLColumnDefinition from master ColumnManager
  protected Vector		columnsList;

  //	to speed up search of columnsList by ColId
  protected Hashtable		byId = new Hashtable();

  /**	actual table columns
   *	subset of columnsList
   *	may be set by column config
   */
  protected Vector		tableColumns = new Vector();

  /**	column collection	*/
  protected Vector		columnSelection = new Vector();

  //	hold copy / delete items
  protected Vector	copyV;

  //	model data modified
  protected boolean	modified = false;

  /**	set of columns with function field set	*/
  protected HashMap   calcCols  = new HashMap();

  /**
   *	together with row class name makes unique string identifying a set of custom user
   *	table configuration columns in database
   */
  protected String clNameId = null;

  public static Color	bg1 = Color.white;
  public static Color	bg2 = new Color( 255, 235, 205 );

  // Format: array of vectors:
  // size of array - number of color schemes = preset 2 only
  // a Map's elements - colors for each column TYPE of a particular scheme of a row.
  public static HashMap bgColors[] = new HashMap[2];


  /**
   *	Constructor
   *
   *	@param rows default # of rows
   *	@param colsV vector of SLColumnDefinition(s) to create table
   */
  public SLTableModel( int rows, Vector colsV )
  {
    //this( rows, colsV, true, null );
	  this( rows, colsV, false, null );
  }

  /**
   *	@param rows default # of rows
   *	@param colsV vector of SLColumnDefinition(s) to create table
   *	@param useUserTblColMgr	boolean	read user table config ???
   */
  public SLTableModel( int rows, Vector colsV, boolean useUserTblColMgr )
  {
    this( rows, colsV, useUserTblColMgr, null );
  }

  /**
   * Constructor
   * 
   * @param rows
   * @param colsV	columnList
   * @param useUserTblColMgr user defined column configuration
   * @param classId
   */
  public SLTableModel( int rows, Vector colsV,  boolean useUserTblColMgr, String classId )
  {
    super( rows, colsV.size() );

    columnsList = colsV;

   //	set up columns with functions( sum, avg )
    for( int i=0; i< colsV.size(); i++ )
    {
      SLColumnDefinition cd = (SLColumnDefinition)colsV.elementAt(i);
      if( cd.getColumnFunction() > 0)
	calcCols.put( cd.getIdName(), cd );
    }

    clNameId = ( classId == null ? "" : classId );

    //	build hash table
    int i;

    for( i = 0; i < columnsList.size(); i++ )
    {
      SLColumnDefinition cd = ( SLColumnDefinition )columnsList.elementAt( i );
      byId.put( new Integer( cd.getId() ), cd );
    }

    //minRows = rows;
    minRows = 0;

    //	keep track of original row indexes
    setRowTracking( true );

    //	renumber data rows on delete
    setDelRowPlaceHolder( false );

    tableColumns = ( Vector )getMasterColumnList().clone();

    if( useUserTblColMgr )
      UserTblColMgr.setUpColumns( this );
  }

  //	construct table from vector of objects
  public SLTableModel( Vector dataV, Vector cols )
  {
    this( dataV.size(), cols );
    setTableData( dataV );
    //sort();
  }

  /**
   *	MUST be defined in sub class
   *	map object fields to vire columns for display
   *	called by TableView
   */
  public abstract Object getFieldAt( Object obj, int c );

  /**
   *	MUST be defined in sub class for editing
   *	called by TableView
   */
  public void setFieldAt( Object o, Object item, int c ) throws Exception
  {}


  /**
   *	override standard TableModel method for editing
   */
  public void setValueAt( Object o, int r, int c )
  {
    if(r<rowData.size()) 
    {
      Object item = itemAtTableRow( r );

      if( item == null )
        return;

      try
      {
	setFieldAt( o, item, c );
      }
      catch( Exception ex )
      {}

      /*
       * a lot of times, update of a cell, triggers updated of other cells,
       * so just reload the entire row
       */
      updateRow(r);

      modified = true;
    }
  }

  /**
   *
   *	format data fields for printing/writing to files
   */
  public Object getPrintFieldAt( int r, int c )
  {
    Object o = getValueAt( r, c );

    if( o != null )
    {
      if( o instanceof java.util.Date )
	return Global.dateOnly( ( java.util.Date )o );

      //	wrap string in quotes
      //if( o instanceof String )
	//return "'" + o + "'";
    }

    return o;
  }

  /**
   *	Called during loading from external sources / import
   *	MUST be implemented in sub class so that the appropriate object can be
   *	created and filled. Takes a list of
   *	Properties( columnId, value )
   *
   *	@param Properties
   */
  public Object genDataItem( Properties p ) throws Exception
  {
    return null;
  }

  /**
   **	return Vector of actual table columns
   */
  public Vector getColumnList()
  {
    return tableColumns;
  }

  /**
   *	set visible columns list
   */
  public void setColumnList( Vector cols )
  {
    tableColumns = cols;
  }

  /**
   *	set column selection vector
   */
  public void setColumnSelectionList( Vector cols )
  {
    columnSelection = cols;
  }

  /**
   **	return Vector of selection columns
   */
  public Vector getColumnSelectionList()
  {
    return columnSelection;
  }

  /**
   *	return vector of ALL available columns
   */
  public Vector getMasterColumnList()
  {
    return columnsList;
  }

  /**
   *	set vector of ALL available columns
   */
  public void setMasterColumnList( Vector v )
  {
    columnsList = v;
  }

  /**
   *	count of possible list of table columns
   */
  public int getMasterColumnCount()
  {
    return columnsList.size();
  }

  /**
   * find column definition by pre-defined ID
   */
  public SLColumnDefinition getById( int id )
  {
    return ( SLColumnDefinition )byId.get( new Integer( id ) );
  }

  /**
   * find column definition by column index
   */
  public SLColumnDefinition getByIndex( int i )
  {
    return ( SLColumnDefinition )tableColumns.elementAt( i );
  }

  /**
   *	get internal column ID based on table column
   *
   *	@param c table column index
   */
  public int getColumnId( int c )
  {
    return (( SLColumnDefinition )tableColumns.elementAt( c )).getId();
  }


  /**
   *	@return index of the visible column for specified column ID
   *
   *	@param columnId
   *
   */
  public int getColumnIndex( int columnId )
  {
    int i;

    for( i = 0; i < tableColumns.size(); i++ )
    {
      SLColumnDefinition cd = ( ( SLColumnDefinition )tableColumns.elementAt( i ) );
      if( columnId == cd.getId() )
	return i;
    }

    return -1;
  }

  /**
   *	index of the column in the master list for specific visible column index
   */
  public int getMasterColumnIndex( int i )
  {
    return columnsList.indexOf( tableColumns.elementAt(i) );
  }

  /**
   *	index of the column in the master list for specific column definition
   */
  public int getMasterColumnIndex( SLColumnDefinition cd )
  {
    int col = columnsList.indexOf( cd );
    return col;
  }

  /**
   * return index of the VISIBLE column
   * @param cd SLColumnDefinition
   * @return int
   */
  public int getVisibleColumnIndex( SLColumnDefinition cd )
  {
    int col = tableColumns.indexOf( cd );
    return col;
  }


  /**
   *	return object from actual data vector
   *
   *	@param r row index
   */
  public Object itemAt(int r)
  {
    if( r < 0 || r >= rowData.size() )
      return null;

    return rowData.elementAt( r );
  }

  //	in case the table was sorted, map to original index
  public Object itemAtTableRow( int r )
  {
    if( r < 0 || r >= rowData.size() )
      return null;

    int or = getOriginalRow( r );

    //	when setting rows for the first time, no mapping exists yet
    if( or < 0 )
      or = r;

    return rowData.elementAt( or );
  }

  //	override base function
  public int getColumnCount()
  {
    if( tableColumns != null )
      return tableColumns.size();
    else
      return 0;
  }

  /**
   **
   **	override base function
   **	return column label from SLColumnDefinition
   */
  public String getColumnName( int c )
  {
    SLColumnDefinition cd = ( ( SLColumnDefinition )tableColumns.elementAt( c ) );
    if( cd != null )
      return cd.getDisplayLabel();
    else
      return null;
  }


  public SLColumnManager getColumnManager()
  {
    return colMgr;
  }

  //	override base function
  public boolean isCellEditable( int r, int c )
  {
    if( r >= rowData.size() )
      return false;

    return ( ( SLColumnDefinition )tableColumns.elementAt( c ) ).isEditable();
  }

  //	overrides default
  public Vector getRowsVector()
  {
    return rowData;
  }

  /**
   **	add row to table
   */
  public void addDataRow( Object obj )
  {
    int row = rowData.size();

    rowData.add( obj );

    Vector v = genTableRowVector( obj );

    //	when empty rows are displayed
    if( row < getRowCount() )
      setRowData( v, row );
    else
      addRow( v );

    sort();

    modified = true;
  }
  
  /**
   * 
   * @param obj user object 
   * @param row of user object to be updated as returned by rowOfTableItem()
   */
  public void updateDataRow(Object obj, int row) {
	  
	  Vector v = genTableRowVector( obj );

	  if(row < getRowCount()) {
    	  setRowData( v, row );
	  }
	  sort();

	  modified = true;
  }

  /**
   *	get row count
   */
  public int size()
  {
    return rowData.size();
  }

  /**
   *	clear table
   */
  public void clear()
  {
    setTableData( new Vector( minRows ) );
  }


  /**
   *	generate vector to represent a row in a table
   *	based on values returned by sub class getFieldAt()
   *
   *	@param r row index in data vector
   */
  public Vector genTableRowVector( int r )
  {

    Object obj = itemAtTableRow( r );

    return genTableRowVector( obj );

  }

  /**
   *	generate vector to represent a row in a table
   *	based on values returned by sub class getFieldAt()
   *
   *	@param obj object to be displayed
   */
  public Vector genTableRowVector( Object obj )
  {
    Vector v = new Vector( getColumnCount() );

    int i;

    for( i = 0; i < getColumnCount(); i++ )
      v.add( getFieldAt( obj, i ) );

    //	store vector ref in the item for later updates
    if( obj instanceof SLDataObjectRowAdaptor )
      ((SLDataObjectRowAdaptor)obj).setRowVector( v );

    return v;
  }

  public void reGenTableData()
  {
    setTableData( rowData );
  }

  /**
   *
   *	Set table data from vector of objects.
   *	the model keeps own copy by cloning the vector
   *	Each row is represented by a vector of cells;
   *	Create vector of rows based on vector argument
   *	Generates vector for every row by calling getFieldAt(r,c) in tableModel
   *
   *	@param table_v	vector of data objects
   */
  public Vector setTableData( Vector table_v )
  {
    if( table_v == null )
    {
      clear();
      return null;
    }

    //	keep own copy of items
      if( table_v != rowData )
	  rowData = (Vector)table_v.clone();

    //	vector or vectors(rows) for display.
    Vector v = new Vector( rowData.size() );

    int i;
    for( i = 0; i < rowData.size(); i++ )
      v.add( genTableRowVector( rowData.elementAt(i) ) );

    for( i = i; i < minRows; i++ )
	v.add( new Vector() );

    if (dataVector != null)
       dataVector.removeAllElements();

    setData( v );
    sort();

    modified = false;

    return v;
  }

  /**
   *	reload row at index r
   */
  public void updateRow( int r )
  {
    Vector v = genTableRowVector( r );
    setRowData( v, r );

    modified = true;
  }

  /**
   *	update a row vector from obj
   */
  public void updateRow( Object obj )
  {
    int i;

    if( obj instanceof SLDataObjectRowAdaptor == false )
      return;

    Vector v = ((SLDataObjectRowAdaptor)obj).getRowVector();

    if( v != null )
      for( i = 0; i < getColumnCount(); i++ )
	v.setElementAt( getFieldAt( obj, i ), i );

    i = getDataVector().indexOf( v );
    fireTableRowsUpdated( i, i );

  }

  /**
  *	index of row representing obj in table
   */
  public int rowTableIndex( Object obj )
  {
    if( obj instanceof SLDataObjectRowAdaptor == false )
      return -1;

    return getDataVector().indexOf( ((SLDataObjectRowAdaptor)obj).getRowVector() );

  }

  /**
   *	update cell by Column ID
   */
  public Object updateCellById( Object o, Vector rowV, int cId )
  {

    int c = getColumnIndex( cId );

    return updateCell( o, rowV, c );

  }

  /**
   *	update cell by table column index
   */
  public Object updateCell( Object o, Vector rowV, int c )
  {

    if( rowV == null )
      return null;

    Object oldO = rowV.elementAt(c);

    rowV.setElementAt( o, c );

    int row = getDataVector().indexOf( rowV );
    fireTableCellUpdated(row, c);

    return oldO;
  }

  /**
   *
   *	Save table in a  comma delimited format
   *
   *	@param outFile File
   */
  public void saveAsCSV( File outFile ) throws Exception
  {
    //	write out column headers
    int c;
    int r;

    StringBuffer buff = new StringBuffer( 1000 );

    PrintWriter out = new PrintWriter( new FileOutputStream( outFile ) );

    //	first write column headers

    for( c = 0; c < getColumnCount(); c++ )
    {
      SLColumnDefinition cd = ( SLColumnDefinition )tableColumns.elementAt( c );
      if( cd.isVisible() )
      {
	if( c == 0 )
	  buff.append( cd.getDisplayLabel() );
	else
	  buff.append( "," ).append( cd.getDisplayLabel() );
      }
    }

    out.println( buff.toString().replace( '\n', ' ') );

    //	write data rows
    for( r = 0; r < rowData.size(); r++ )
    {
      buff = new StringBuffer( 1000 );
      for( c = 0; c < getColumnCount(); c++ )
      {
	SLColumnDefinition cd = ( SLColumnDefinition )tableColumns.elementAt( c );
	if( cd.isVisible() == false )
	  continue;

	Object obj = getPrintFieldAt( r, c );

	if( c > 0 )
	  buff.append( "," );

	//	remove commas from strings so not to confuse csv file
	if( obj != null )
	    buff.append( obj.toString().replace( ',',' ') );
      }

      out.println( buff.toString() );
    }

    out.close();

  }


  /**
   *
   *	Save table as an excel temp file and start Excel
   *
   */
  public void doExcel() throws Exception
  {

    File outFile = File.createTempFile( "iDelta", ".xls" );

    saveAsXls( outFile.getCanonicalPath() );

    Global.runApplication( Global.APP_CMD, " \"" + outFile.getCanonicalPath() + "\"" );

  }


  /**
   *
   *	Save table in a  Tab delimited format
   *
   *	@param outFile File
   */
  public void saveAsTxt( File outFile ) throws Exception
  {
    //	write out column headers
    int c;
    int r;

    StringBuffer buff = new StringBuffer( 1000 );

    PrintWriter out = new PrintWriter( new FileOutputStream( outFile ) );

    //	first write column headers

    for( c = 0; c < getColumnCount(); c++ )
    {
      SLColumnDefinition cd = ( SLColumnDefinition )tableColumns.elementAt( c );
      if( cd.isVisible() )
      {
	if( c == 0 )
	  buff.append( cd.getDisplayLabel() );
	else
	  buff.append( "\t" ).append( cd.getDisplayLabel() );
      }
    }

    out.println( buff.toString() );

    //	write data rows
    for( r = 0; r < rowData.size(); r++ )
    {
      buff = new StringBuffer( 1000 );
      for( c = 0; c < getColumnCount(); c++ )
      {
	SLColumnDefinition cd = ( SLColumnDefinition )tableColumns.elementAt( c );
	if( cd.isVisible() == false )
	  continue;
	Object obj = getPrintFieldAt( r, c );

	if( c > 0 )
	  buff.append( "\t" );
	if( obj != null )
	    buff.append( obj.toString() );

      }

      out.println( buff.toString() );
    }

    out.close();
  }

  /**
   *
   *	Save table as a excel spreadsheet
   *
   *	@param fileName
   */
  public void saveAsXls( String fileName ) throws Exception
  {
    BookModelImpl xls = new BookModelImpl();
    xls.initWorkbook();

    //	write out column headers
    int c;
    int r;
    int xlsRow = 0;

    //	first write column headers

    for( c = 0; c < getColumnCount(); c++ )
    {
      SLColumnDefinition cd = ( SLColumnDefinition )tableColumns.elementAt( c );
      xls.setText(xlsRow, c, cd.getDisplayLabel() );
    }
    xlsRow++;
    //	write data rows
    for( r = 0; r < rowData.size(); r++, xlsRow++ )
    {
      for( c = 0; c < getColumnCount(); c++ )
      {
	SLColumnDefinition cd = ( SLColumnDefinition )tableColumns.elementAt( c );
	if( cd.isVisible() == false )
	  continue;
	Object obj = getPrintFieldAt( r, c );
        if (obj != null)
        {
          if (obj instanceof Number )
            xls.setNumber(xlsRow, c, ((Number)obj).doubleValue() );
          /*else if (obj instanceof Long || obj instanceof Integer)
            xls.setNumber(xlsRow, c, ((Number)obj).longValue() );
            */
          else 
            xls.setText(xlsRow, c, obj.toString().trim() );
        }
      }
    }
    // write out file in Excel 97 format
    xls.write(fileName, BookModel.eFileExcel97);

  }


  /**
   **	Read file in csv format
   **	Values are mapped to table columns using headers in the first record.
   **
   **	The row objects are set by calling genDataItem() implimented by the
   **	subclass
   */
  public Vector readAsCSV( File inFile ) throws Exception
  {

    int c;
    int r;

    BufferedReader in = new BufferedReader( new FileReader( inFile ) );

    //	first read column ids

    String lineStr = in.readLine();

    StringTokenizer tk = new StringTokenizer( lineStr, "," );

    int col_cnt = 0;
    String[] colId = new String[ 100 ];

    //rowData = new Vector();

    Properties dataMap = new Properties();


    while( tk.hasMoreTokens() )
    {
      String idName = tk.nextToken();
      dataMap.setProperty( idName, "" );
      colId[ col_cnt ] = idName;
      col_cnt++;
    }

    //	read row values
    Vector v = new Vector();

    while( true )
    {
      //	read next line from file
      lineStr = in.readLine();
      if( lineStr == null )
	break;

      tk = new StringTokenizer( lineStr, "," );

      int i;
      for( i=0; i < col_cnt; i++ )
	dataMap.setProperty( colId[ i ], tk.nextToken() );

      Object obj = genDataItem( dataMap );

      if( obj != null )
	v.add( obj );
    }

    setTableData( v );
    return v;

  }

  /**
   *	copy vector of provided items to <i>copyV</i>
   *	copy to clipboard when copyToCB parameter is true
   */
  public int copyTableRows( int[] rows, boolean copyToCB )
  {

    if( rows.length <= 0 )
      return 0;

    copyV = new Vector(rows.length);

    int i;

    StringBuffer sb = new StringBuffer( 5000 );

    //	first write column headers
    for( i = 0; i < getColumnCount(); i++ )
    {
      SLColumnDefinition cd;
      cd = ( SLColumnDefinition )tableColumns.elementAt( i );
      if( i == 0 )
	sb.append( cd.getIdName() );
      else
	sb.append( "\t" ).append( cd.getIdName() );
    }

    sb.append( "\n" );

    //	now write out data rows
    for( i = 0; i < rows.length; i++ )
    {

      copyV.add( itemAtTableRow( rows[i]) );

      if( copyToCB == true )
      {
	Vector rData = getRowData( rows[i] );

	int j;
	for( j = 0; j < rData.size(); j++ )
	{
	  Object o = rData.elementAt(j);
	  if( o != null )
          {
            //  dates must be formated
	    if( o instanceof java.util.Date )
// 	      o = DateFormat.getDateInstance(DateFormat.SHORT).format(o);
	      o = Global.dateOnly( ( java.util.Date )o );

	    if( j == 0 )
	      sb.append( o.toString() );
	    else
	      sb.append( "\t" ).append( o.toString() );
          }
	  else
	    if( j > 0 )
	      sb.append( "\t" );
	}

	sb.append( "\n" );
      }

    }


    //	add rows to clipboard
    if( copyToCB == true )
    {
      Clipboard clpb = Toolkit.getDefaultToolkit().getSystemClipboard();
      StringSelection sel = new StringSelection( sb.toString() );
      clpb.setContents( sel, null );
    }

    return rows.length;

  }


  /**
   *	delete row object
   */
  public Object delete( SLDataObject dataO )
  {
    if( dataO instanceof SLDataObjectRowAdaptor == false )
      return null;

    int r = rowTableIndex( dataO );
    if( r < 0 )
      return null;

    return delete( r );
  }

  /**
   *	delete row at index r and return deleted object
   */
  public Object delete( int r )
  {

    if( r < 0 || r >= rowData.size() )
      return null;

    int[] ra = new int[1];

    ra[0] = r;

    deleteTableRows( ra );

    modified = true;

    fireTableRowsDeleted( r, r );

    return copyV.elementAt( 0 );


  }


  /**
   *	delete table rows
   *	in case table was sorted, make sure to remove data rows
   *	based on original index
   */
  public int deleteTableRows( int[] rows )
  {

    if( rows.length <= 0 )
      return 0;

    copyV = new Vector(rows.length);

    //	map table rows to data rows
    int[] trows = new int[rows.length];

    int i;
    for( i = 0; i < rows.length; i++ )
      trows[i] = getOriginalRow( rows[i] );

    int j;

    //	first remove and copy from data vector
    for( i = rows.length - 1; i >= 0 ; i-- )
    {
      copyV.add( rowData.remove( trows[i] ) );
      for( j = 0; j < i; j++ )
	if( trows[j] > trows[i] )
	  trows[j]--;
    }

    //	now remove from table
    for( i = rows.length - 1; i >= 0 ; i-- )
      removeRow( rows[i] );

    return rows.length;

  }


  /*
  public int paste()
  {
    if( copyV == null )
      return 0;

    int i;
    for( i=0; i < copyV.size(); i++)
    {
      Object o = copyV.elementAt(i).clone();
      addDataRow( o );
    }

  }
  */

  public int rowOfTableItem (Object obj)
  {
    for (int row = 0; row < getRowCount(); row++)
     {
        Object currentObj = itemAtTableRow(row);
        if (obj.equals(currentObj))
          return row;
      }
      return -1;
   }

  public Vector getCopyVector()
  {
    return copyV;
  }


  public boolean isModified()
  {
    return modified;
  }

  public void setModified( boolean mod )
  {
    modified = mod;
  }

  /**
   *
   * alter background color after sort
   */
  public void setAlterBgOnSort()
  {
    setAlterBgOnSort( bg2 );
  }

  /**
	 * 
	 * alter background color to altBg after sort
	 */
	public void setAlterBgOnSort(Color altBg) {
		bg2 = altBg;
		addTableModelListener(new TableModelListener() {
			public void tableChanged(TableModelEvent e) {
				int tp = e.getType();
				if (tp == PVTableModel.SORT_END) {

					int c = e.getColumn();
					Color currentColor = bg1;
					int r;
					Object prevObj = getValueAt(0, c);
					Object o;

					if (itemAtTableRow(0) instanceof SLDataObjectRowAdaptor) {
						((SLDataObjectRowAdaptor) itemAtTableRow(0))
								.setSortBackground(currentColor);
						for (r = 1; r < rowData.size(); r++) {
							o = getValueAt(r, c);

							// prevObj != o this is done in case one of them is
							// null
							if (prevObj != o && prevObj.equals(o) == false) {
								currentColor = (currentColor.equals(bg1) ? bg2
										: bg1);
								prevObj = o;
							}

							((SLDataObjectRowAdaptor) itemAtTableRow(r))
									.setSortBackground(currentColor);

						}
					} else if (itemAtTableRow(0) instanceof SLDataObjectAdaptor) {
						((SLDataObjectAdaptor) itemAtTableRow(0))
								.setSortBackgroundColorCode(new SLColor(
										currentColor.getRed(), currentColor
												.getGreen(), currentColor
												.getBlue()));
						for (r = 1; r < rowData.size(); r++) {
							o = getValueAt(r, c);

							// prevObj != o this is done in case one of them is
							// null
							if (prevObj != o && prevObj.equals(o) == false) {
								currentColor = (currentColor.equals(bg1) ? bg2
										: bg1);
								prevObj = o;
							}

							((SLDataObjectAdaptor) itemAtTableRow(r))
									.setSortBackgroundColorCode(new SLColor(
											currentColor.getRed(), currentColor
													.getGreen(), currentColor
													.getBlue()));

						}
					}
				} else {
					return;
				}
			}
		});
	}

  public void setDefaultSortCols()
  {
  }

  public void setDefaultSortCols( ColumnSortDescriptor[] defaultSortCols )
  {
    defaultSortColumns = defaultSortCols;

    int[] sortCols = getSortColumns();
    if( sortCols[ 0 ] == -1 && defaultSortColumns != null )
    {
      for( int j = 0; j < defaultSortColumns.length && j < 3; ++j )
      {
	setSortingAt( getColumnIndex( defaultSortColumns[ j ].colId ),
		      defaultSortColumns[ j ].priority,
		      defaultSortColumns[ j ].ascending,
		      defaultSortColumns[ j ].type );
      }
    }

    if( getRowCount() > 1 )
      sort();
  }

  // Used in UserTblColMgr to distinguish between regular tm and GenericTableModel
  public String getClassNameId() {
    return clNameId;
  }

  public void setClassNameId( String s )
  {
    clNameId = s;
  }

  /**
   * process SLDataChangeEvent
   */
  public void onDataChange( SLDataChangeEvent e )
  {
    switch( e.reason )
    {
      case SLDataChangeEvent.RECORD_UPDATE:
	updateRow( (SLDataObject)e.getDataChangeObj() );
	break;
      case SLDataChangeEvent.RECORD_ADD:
	addDataRow( (SLDataObject)e.getDataChangeObj() );
	break;
      case SLDataChangeEvent.RECORD_DELETE:
	delete( (SLDataObject)e.getDataChangeObj() );
	break;
      case SLDataChangeEvent.DATA_CHANGE:
	setTableData( (Vector)e.getDataChangeObj() );
	break;

    }
  }

  /**
   *  Sets the editability of a column identified by its column ID
   *
   *  @param colId the internal column identifier
   *  @param val true makes column editable, false makes column not editable
   */
  public void setColumnEditable( int colId, boolean val )
  {
    setColumnEditableByColNum( getColumnIndex( colId ), val );
  }

  /**
   *  Sets the editability of a column specified by its absolute position
   *
   *  @param colNum the column position
   *  @param val true makes column editable, false makes column not editable
   */
  public void setColumnEditableByColNum( int colNum, boolean val )
  {
    if( colNum == -1 )
      return;

    SLColumnDefinition cd = null;
    try
    {
      cd = ( ( SLColumnDefinition )tableColumns.elementAt( colNum ) );
    }
    catch( ArrayIndexOutOfBoundsException ex ) {}

    if( cd == null )
      return;

    cd.setEditable( val );
  }

  /**
   * paste into table from Clipboard.
   * column order was set prior, by ColumnSelection dialog or other
   * when column order not set, use display order
   * Read one line at time from clipboard, build column/value Properties,
   * call genDataItem(...) defined in sub class to generate row object
   * add row to table
   */
  public Vector pasteFromClipBoard() throws Exception
  {

    Clipboard clpb = Toolkit.getDefaultToolkit().getSystemClipboard();
    Transferable contents = clpb.getContents( this );

    if( contents == null )
      return null;


    int c;
    int r;

    int col_cnt = 0;
    String[] colId = new String[ 100 ];

    Properties dataMap = new Properties();

    // when paste columns are not set, use table columns
    Vector cv = ( ( columnSelection == null || columnSelection.size() == 0 ) ? tableColumns : columnSelection );

    //	put column ids into properties
    for( int i=0; i < cv.size(); i++ )
    {
      SLColumnDefinition cd = ( SLColumnDefinition )cv.elementAt( i );
      colId[ col_cnt ] = Integer.toString( cd.getId() );
      dataMap.setProperty( colId[ col_cnt ], EMPTY_CELL );

      col_cnt++;
    }

    //	read row values
    Vector v = new Vector();

    //	keep track of multiple row exceptions
    StringBuffer sbuff = new StringBuffer(1000);

    String text;

    // get paste buffer
    text = (String)(contents.getTransferData(DataFlavor.stringFlavor));

    // parse one line at a time
    StringTokenizer linetk = new StringTokenizer( text, "\n" );

    /*
     *	read each line, parse it using TAB as a seperator
     *	call genDataItem(...) to generate row
     */
    String str;
    int rowCnt = 0;
    while( linetk.hasMoreTokens() )
    {
      rowCnt++;
      str = linetk.nextToken();
      // parse fields
      StringTokenizer tk = new StringTokenizer( str, "\t" );

      int i = 0;
      while( tk.hasMoreTokens() )
	dataMap.setProperty( colId[ i++ ], tk.nextToken() );

      Object obj = null;
      try
      {
	obj = genDataItem( dataMap );
      }
      catch( Exception ex )
      {
	sbuff.append( "Row: " + rowCnt + ", " + ex.getMessage() + "\n" );
	Util.log( "pasteFromClipBoard","failed, Row: " + rowCnt + ", " + ex.getMessage() );

	if( sbuff.length() > 900 )
	{
	  sbuff.append( "Too many errors. Operation Canceled" );
	  throw new Exception( sbuff.toString() );
	}
	continue;
      }

      if( obj != null )
	v.add( obj );
    }

    setTableData( v );

    if( sbuff.length() > 0 )
      throw new Exception( sbuff.toString() );

    postPaste();

    return v;

  }


  public HashMap getCalcCols()
  {
    return calcCols;
  }

  public int getColumnFunction(int col)
  {
    SLColumnDefinition rcd = (SLColumnDefinition) byId.get( new Integer(col) );
    if (rcd == null) return -1;
    return rcd.getColumnFunction();
  }

  /**
   * Apply standard column functions and fill in totals row
   * @return GenericRow
   *
   * This method can be overloaded by the extending class to calculate "summary" value
   * for the column, i.e. WA Rate.
   * For an example of calculation of WA borrow and WA client rates see idelta/clientposition/RateDetailTableModel.java.
   *
   */
  public GenericRow calcTotals()
  {

    if( calcCols == null || calcCols.size() <= 0 || getDataVector().size() == 0 )
      return new GenericRow( this );

    GenericRow totRow = new GenericRow( this );

    int col;
    Vector rowVec = null;
    Object calcVec [] = calcCols.values().toArray();
    //	displayed table rows( as Vectors of cells )
    Vector dataVec = getDataVector();

    int cnt[] = new int[ tableColumns.size() ];

    for( int i=0; i<tableColumns.size(); i++ )
      cnt[i] = 0;

    /*
     *	for either SUM of AVERAGE, need to add up all values
     *	keep count for AVERAGE
     */
    for(int r = 0; r < dataVec.size(); r++)
    {
      rowVec = (Vector) dataVec.elementAt( r );
      if( rowVec == null )
        continue;
      for(int l = 0; l < calcVec.length; l++)
      {
      	SLColumnDefinition rcd = (SLColumnDefinition) calcVec[l];

	int func = rcd.getColumnFunction();
	if (func > 0)
	{
		if( (col = getVisibleColumnIndex( rcd )) < 0 )
		  continue;

	  Object o1 = totRow.getField( col );
    o1 = (o1 != null ? o1 : new Double(0.0));

	  Object o2 = rowVec.elementAt( col );
    o2 = ( o2 != null ? o2 : new Double(0.0) );
	  if( o1 instanceof java.lang.Number && o2 instanceof java.lang.Number )
	  {
	    Number n1 = (Number) ( o1 );
	    Number n2 = (Number) ( o2 );

	    totRow.setField( col, new Double( n1.doubleValue() + n2.doubleValue() ) );

	    if( func == SLColumnDefinition.FUNCTION_AVERAGE && n2.doubleValue() != 0.0 )
	      cnt[ col ] = cnt[ col ] + 1;
	  }
	}
      }
    }

    for( int i=0; i < tableColumns.size(); i++ )
      if( cnt[i] > 1 )
	totRow.setField( i, new Double( ((Number)totRow.getField( i )).doubleValue() / cnt[i] ) );

    return totRow;

  }

  /**
   * method can be overloaded to provide custom toolTips by classes extending SLTableModel
   * called from SLTable.createDefaultTableHeader()
   */
  public String getHeaderToolTip( int colIndex )
  {
    try
    {
      return( ( SLColumnDefinition )getColumnList().elementAt( colIndex ) ).getHeaderToolTip();
    }
    catch( Exception ex )
    {
      return null;
    }
  }

  /**
   *	method called at the end of paste operation
   *	to be overloaded by extending class
   */
  public void postPaste()
  {}

  /**
   * Generally all TableModels would be having 
   * Exclude option and we would need this functionality to
   * be declared here to aviod redundancy in child classes for
   * its action listener
   * 
   * @param selectedRows
   */
  public void toggleExcluded(int[] selectedRows) {
  }
}
